package com.jindi.jacococli.biz.utils;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;


public class DateUtils {

    public enum DateTimeFormatConstant {
        YYYY_MM_DD(1, "yyyy-MM-dd"),
        YYYY_MM_DD_HH_MM_SS(2, "yyyy-MM-dd HH:mm:ss"),
        YYYY(1, "yyyy"),
        YYYYMM(1, "yyyyMM"),
        YYYYMMDD(1, "yyyyMMdd"),
        YYYYMMDDHH(2, "yyyyMMddHH"),
        YYYYMMDDHHMM(2, "yyyyMMddHHmm"),
        YYYYMMDDHHMMSS(2, "yyyyMMddHHmmss"),
        YYYY_UD_MM_UD_DD(1, "yyyy/MM/dd");

        public static final Integer DATE = 1;
        public static final Integer DATETIME = 2;
        private Integer type;
        private String format;

        DateTimeFormatConstant(Integer type, String format) {
            this.type = type;
            this.format = format;
        }

        public Integer getType() {
            return type;
        }

        public void setType(Integer type) {
            this.type = type;
        }

        public String getFormat() {
            return format;
        }

        public void setFormat(String format) {
            this.format = format;
        }
    }

    private static final Map<DateTimeFormatConstant, DateTimeFormatter> CACHE = new ConcurrentHashMap<>();

    /**
     * 当前时间的字符串形式
     *
     * @return
     */
    public static String nowDateTimeString() {
        return get(DateTimeFormatConstant.YYYY_MM_DD_HH_MM_SS).format(LocalDateTime.now());
    }

    /**
     * 当前日期的字符串形式
     *
     * @return
     */
    public static String nowDateString() {
        return get(DateTimeFormatConstant.YYYY_MM_DD).format(LocalDate.now());
    }

    /**
     * 日期转字符串
     *
     * @param date
     * @param dateTimeFormat
     * @return
     */
    public static String dateTimeString(Date date, DateTimeFormatConstant dateTimeFormat) {
        if (date == null) {
            return null;
        }
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        if (Objects.equals(DateTimeFormatConstant.DATE, dateTimeFormat.getType())) {
            LocalDate localDate = localDateTime.toLocalDate();
            return get(dateTimeFormat).format(localDate);
        }
        return get(dateTimeFormat).format(localDateTime);
    }

    /**
     * 字符串转日期对象
     *
     * @param str
     * @param dateTimeFormat
     * @return
     */
    public static Date parse(String str, DateTimeFormatConstant dateTimeFormat) {
        if (StringUtils.isBlank(str)) {
            return null;
        }
        ZoneId zone = ZoneId.systemDefault();
        if (Objects.equals(DateTimeFormatConstant.DATE, dateTimeFormat.getType())) {
            return Date.from(LocalDate.parse(str, get(dateTimeFormat)).atStartOfDay(zone).toInstant());
        }
        return Date.from(LocalDateTime.parse(str, get(dateTimeFormat)).atZone(zone).toInstant());
    }

    /**
     * @param dateTimeFormat
     * @return
     */
    private static DateTimeFormatter get(DateTimeFormatConstant dateTimeFormat) {
        if (CACHE.containsKey(dateTimeFormat)) {
            return CACHE.get(dateTimeFormat);
        }
        CACHE.put(dateTimeFormat, DateTimeFormatter.ofPattern(dateTimeFormat.getFormat()));
        return CACHE.get(dateTimeFormat);
    }

    public static Date addYears(Date date, int amount) {
        return add(date, 1, amount);
    }

    public static Date addMonths(Date date, int amount) {
        return add(date, Calendar.YEAR, amount);
    }

    public static Date addWeeks(Date date, int amount) {
        return add(date, Calendar.WEEK_OF_YEAR, amount);
    }

    public static Date addDays(Date date, int amount) {
        return add(date, Calendar.DAY_OF_MONTH, amount);
    }

    public static Date addHours(Date date, int amount) {
        return add(date, Calendar.HOUR_OF_DAY, amount);
    }

    public static Date addMinutes(Date date, int amount) {
        return add(date, Calendar.MINUTE, amount);
    }

    public static Date addSeconds(Date date, int amount) {
        return add(date, Calendar.SECOND, amount);
    }

    public static Date addMilliseconds(Date date, int amount) {
        return add(date, Calendar.MILLISECOND, amount);
    }

    public static Date setYears(Date date, int amount) {
        return set(date, Calendar.YEAR, amount);
    }

    public static Date setMonths(Date date, int amount) {
        return set(date, Calendar.MONTH, amount);
    }

    public static Date setDays(Date date, int amount) {
        return set(date, Calendar.DAY_OF_MONTH, amount);
    }

    public static Date setHours(Date date, int amount) {
        return set(date, Calendar.HOUR_OF_DAY, amount);
    }

    public static Date setMinutes(Date date, int amount) {
        return set(date, Calendar.MINUTE, amount);
    }

    public static Date setSeconds(Date date, int amount) {
        return set(date, Calendar.SECOND, amount);
    }

    public static Date setMilliseconds(Date date, int amount) {
        return set(date, Calendar.MILLISECOND, amount);
    }

    private static Date add(Date date, int calendarField, int amount) {
        if (date == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(calendarField, amount);
        return c.getTime();
    }

    private static Date set(Date date, int calendarField, int amount) {
        if (date == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setLenient(false);
        c.setTime(date);
        c.set(calendarField, amount);
        return c.getTime();
    }
}
